# koa-static中间件使用
30 秒速记
- 一行搞定:
app.use(koaStatic(path.join(__dirname, 'static'))),内部用koa-send发文件 root只指向专门放公开资源的目录,别指向项目根,不然源码和.env都能被下载- 只处理
GET和HEAD;找不到文件会await next()交给后面的中间件 - 常用配置:
maxage缓存时长、immutable、index默认首页、hidden是否允许隐藏文件(默认不允许)、defer是否让路由先处理 - 目录穿越
koa-send已经拦了,不用自己再写
koa-static 就是把上一节手写的静态服务器封装成了中间件,一行代码就能托管一个目录。 它内部用 koa-send 发文件,流式输出、Content-Type、Last-Modified、目录穿越防护都处理了。用的时候要注意两件事:root 一定要指向专门的公开目录,指向项目根就等于把源码和配置文件都放出去了;文件找不到时它不会返回 404,而是交给后面的中间件,所以后面如果有个兜底返回 hello world,缺失的图片也会拿到 200 和这段文字。
理解「koa-static中间件使用」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。
工程中还要验证退出码、资源清理和异常输入。属于 middleware 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。
面试官追问
追问 1请求 /missing.png 也返回了 hello world,是 koa-static 出问题了吗?
不是,这是它的设计。找不到文件就 await next(),后面的兜底中间件给了 body,状态码就是 200。兜底中间件要区分路径,静态资源找不到应该返回 404。
追问 2root 传 './static' 和 path.join(__dirname, 'static') 有区别吗?
有。相对路径按 process.cwd() 解析,从别的目录启动就找错地方;__dirname 是当前文件所在目录,在哪启动都一样。线上统一用绝对路径。
追问 3前端打包出来的带哈希的文件,缓存怎么配?
koaStatic(root, { maxage: 365 * 24 * 3600 * 1000, immutable: true }),maxage 单位是毫秒。index.html 不带哈希,要单独让它不缓存或短缓存,不然发版后用户拿不到新页面。
追问 4想让 /api 路由优先、静态文件兜底,怎么配?
把 koa-static 注册在路由后面,或者传 defer: true,让它先 await next(),后面没人设置响应时再去找文件。默认是先找文件,接口路径和文件同名时文件会赢。
追问 5线上要不要让 Koa 自己托管所有静态资源?
小项目、内部工具这样很省事。流量大的站点我会把静态资源放到 CDN 或 Nginx,Node 进程只管接口,发版、缓存、带宽都更好控制。
# koa-static中间件使用
# 使用例子
30 秒速记
koa-static(root, opts):root用path.join(__dirname, 'static')锚定成绝对路径,别依赖启动目录- 只处理
GET/HEAD;找到文件就直接响应,找不到才await next()交给后面的中间件 - 常用选项:
maxage(毫秒)、index(默认index.html)、hidden(默认不发点文件)、gzip/br(发预压缩文件)、defer root只放公开资源,别指向项目根目录,否则package.json、源码都能被直接下载const static = ...在严格模式和ESM里会报错,变量名换成serve
koa-static 就是一个「先去目录里找文件,找到就返回,找不到就放行」的中间件。 示例里它挂在最前面,访问 /index.html、/js/index.js 能直接拿到文件;访问一个不存在的路径,它找不到就 next(),请求落到后面那个写 hello world 的中间件。所以看到 hello world 不代表文件存在,反而说明静态目录没命中。目录我一定用 __dirname 拼绝对路径,因为相对路径是相对进程启动目录算的,换个目录 node xxx/index.js 就全挂。上线时我还会给带内容 hash 的资源配长 maxage,或者干脆把静态资源交给 Nginx / CDN。
demo源码
const Koa = require('koa')
const path = require('path')
const static = require('koa-static')
const app = new Koa()
// 静态资源目录对于相对入口文件index.js的路径
const staticPath = './static'
app.use(static(
path.join( __dirname, staticPath)
))
app.use( async ( ctx ) => {
ctx.body = 'hello world'
})
app.listen(3000, () => {
console.log('[demo] static-use-middleware is starting at port 3000')
})
面试官追问
追问 1同事把 './static' 直接传给 koa-static,在项目根目录启动没问题,用 pm2 从别的目录启动就 404 了,为什么?
相对路径是按 process.cwd() 解析的,不是按 index.js 所在目录。启动目录一变,./static 指向的就是另一个地方。用 path.join(__dirname, 'static') 锚定到文件位置就稳了。
追问 2访问一个不存在的 /missing.png,返回的是 200 和 hello world,这算正常吗?
对这段代码来说是预期行为:koa-static 没找到文件就放行,后面的中间件无条件写了 body,Koa 自动给 200。真实项目里兜底中间件要按路径判断,图片、脚本这种资源请求找不到就该老老实实返回 404,不然前端排查会被误导。
追问 3想让业务路由优先,静态文件兜底,怎么配?
两种办法:把 koa-static 挪到路由后面,或者保留原位置但开 defer: true。defer 的意思是先 await next() 跑完后面的中间件,只有响应还是空的(404)时才去读文件。
追问 4有人请求 /../package.json,能把项目配置下载走吗?
koa-static 底层用的 koa-send 会把路径限制在 root 里,路径穿越会被拦掉。真正的风险是 root 本身配错,比如直接指向项目根目录,那 package.json 和源码都能直接访问,hidden 只挡得住 .env 这类点文件。
追问 5静态资源每次都重新下载,怎么让浏览器缓存?
加 maxage,单位是毫秒,比如 serve(dir, { maxage: 365 * 24 * 3600 * 1000 })。前提是文件名带内容 hash,不然发版后用户拿到的还是旧文件;index.html 这种入口文件要单独设短缓存或不缓存。
# 效果
# 访问http://localhost:3000

# 访问http://localhost:3000/index.html

# 访问http://localhost:3000/js/index.js

